__author__ = 'shawhen'

from ..platform import what as m_what_platform


class UProcess():
    def __init__(self):
        self._process_handle = None

        self._process_stdout_pipe_r = None
        self._process_stdout_pipe_w = None
        self._process_stdin_pipe_r = None
        self._process_stdin_pipe_w = None

        self._work_dir = None

        self._state = "blank"  # "blank"/"started"

        self._heartbeat_thread = None

    def start(self, commandline):
        if self._state != "blank":
            sys.stderr.write("start a started process, this will kill the old one")
            sys.stderr.flush()

            self.kill()

        if m_what_platform.running_os["type"] == "linux":
            import ctypes

            from ..sysapi.linux import unistd as m_unistd

            process_stdin_pipe_r, process_stdin_pipe_w = m_unistd.pipe()
            if process_stdin_pipe_r is None or process_stdin_pipe_w is None:
                raise ValueError("create process stdin pipe failed, ec: {0}".format(ctypes.get_errno()))
            process_stdout_pipe_r, process_stdout_pipe_w = m_unistd.pipe()
            if process_stdout_pipe_r is None or process_stdout_pipe_w is None:
                raise ValueError("create process stdout pipe failed, ec: {0}".format(ctypes.get_errno()))

            pid = m_unistd.fork()
            if pid < 0:
                raise ValueError("fork failed")
            elif pid == 0:  # 这里是子进程入口
                dup_result = m_unistd.dup2(process_stdin_pipe_r, m_unistd.STDIN_FILENO)
                if dup_result == -1:
                    raise ValueError("dup2 stdin pipe r->STDIN_FILENO failed,ec: {0}".format(ctypes.get_errno()))
                dup_result = m_unistd.dup2(process_stdout_pipe_w, m_unistd.STDOUT_FILENO)
                if dup_result == -1:
                    raise ValueError("dup2 stdout pipe w->STDOUT_FILENO failed,ec: {0}".format(ctypes.get_errno()))
                dup_result = m_unistd.dup2(process_stdout_pipe_w, m_unistd.STDERR_FILENO)
                if dup_result == -1:
                    raise ValueError("dup2 stdout pipe w->STDERR_FILENO failed,ec: {0}".format(ctypes.get_errno()))
                close_result = m_unistd.close(process_stdin_pipe_r)
                if close_result == -1:
                    raise ValueError("close stdin pipe r failed,ec: {0}".format(ctypes.get_errno()))
                close_result = m_unistd.close(process_stdout_pipe_w)
                if close_result == -1:
                    raise ValueError("close stdout pipe w failed,ec: {0}".format(ctypes.get_errno()))

                m_unistd.close(process_stdin_pipe_w)
                m_unistd.close(process_stdout_pipe_r)

                # 将命令行转化为参数列表
                command_list = []
                start_pos = 0
                if commandline[0] == '"':
                    last_quoto_pos = 0
                else:
                    last_quoto_pos = -1
                while True:
                    space_pos = commandline.find(" ", start_pos)
                    if space_pos == -1:  # the last param
                        if commandline[-1] == '"':
                            cmd = commandline[last_quoto_pos+1:-1]
                        else:
                            cmd = commandline[start_pos:]
                        command_list.append(cmd)

                        break
                    if commandline[space_pos-1] != '"':
                        cmd = commandline[start_pos:space_pos]
                        command_list.append(cmd)
                    else:  # " in space left
                        cmd = commandline[last_quoto_pos+1:space_pos-1]
                        command_list.append(cmd)

                        last_quoto_pos = -1

                    if commandline[space_pos+1] == '"':  # " in space right
                        last_quoto_pos = space_pos+1

                    start_pos = space_pos+1

                # 改变工作路径
                if self._work_dir is not None:
                    m_unistd.chdir(self._work_dir)
                result = m_unistd.execvp(command_list[0], command_list[1:])
                if result < 0:
                    raise ValueError("execvp({0} with argv:{1}) failed,ec: {2}".format(command_list[0], command_list[1:], ctypes.get_errno()))
            else:  # 父进程入口
                self._process_handle = pid

                m_unistd.close(process_stdin_pipe_r)
                m_unistd.close(process_stdout_pipe_w)
                self._process_stdin_pipe_w = process_stdin_pipe_w
                self._process_stdout_pipe_r = process_stdout_pipe_r
        else:
            raise NotImplementedError

        self._state = "started"

    def native_handle(self):
        return self._process_handle

    def id(self):
        if m_what_platform.running_os["type"] == "linux":
            return self._process_handle
        else:
            raise NotImplementedError

    def setWorkingDirectory(self, work_dir):
        self._work_dir = work_dir

    def read(self, bytes_to_read):
        """
        :return (True/False, read buf/None)
        :note no exception inside
        """
        if self._state != "started":
            raise ValueError("read blank process")
        if m_what_platform.running_os["type"] == "linux":
            from ..sysapi.linux import unistd

            read_result = unistd.read(self._process_stdout_pipe_r, bytes_to_read)
            if read_result[0] == 0:
                return True, read_result[1]
            else:
                return False, None
        else:
            raise NotImplementedError

    def write(self, data):
        """
        :return (True/False, bytes written)
        """
        if self._state != "started":
            raise ValueError("write blank process")
        data_bytes = None
        if isinstance(data, bytes):
            data_bytes = data
        elif isinstance(data, str):
            data_bytes = data.encode()
        if m_what_platform.running_os["type"] == "linux":
            from ..sysapi.linux import unistd

            write_result = unistd.write(self._process_stdin_pipe_w, data_bytes)
            if write_result == -1:
                return False, -1
            else:
                return True, write_result
        else:
            raise NotImplementedError

    def waitForFinished(self, wait_seconds):
        raise NotImplementedError

    def waitForStarted(self, wait_seconds):
        """
        等待进程完成初始化，
        异常：
            TimeoutError, WindowsError
        """
        raise NotImplementedError